/*
    This file is part of QTau
    Copyright (C) 2013-2018  Tobias "Tomoko" Platen <tplaten@posteo.de>
    Copyright (C) 2013       digited       <https://github.com/digited>
    Copyright (C) 2010-2013  HAL@ShurabaP  <https://github.com/haruneko>

    QTau is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    SPDX-License-Identifier: GPL-3.0+
*/

#include "ecantorix_synth.h"
#include "../editor/ustjkeys.h"

#include <QFileInfo>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QDirIterator>
#include <QStringList>
#include <QJsonDocument>

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <QProcess>
#include <QtConcurrent/QtConcurrent>
#include <sndfile.h>
#include <unistd.h>

#define __devloglevel__ 4

//utilityfunctions -- move to libkawaii
bool fileExists(QString path) {
    QFileInfo check_file(path);
    // check if file exists and if yes: Is it really a file and no directory?
    return check_file.exists() && check_file.isFile();
}

//ecantorix synth specific

/// manifest
QString eCantorixSynth::name()        { return "eCantorix2"; }
QString eCantorixSynth::description() { return "A multilingual KTH-style singing synthesizer"; }
QString eCantorixSynth::version()     { return "18.04"; }


/// setup
void eCantorixSynth::setup(IController* ctrl)      {
    this->_ctrl = ctrl;
    this->_jack_samplerate = ctrl->sampleRate();

    //thread queue signaling
    connect(this,&eCantorixSynth::logDebug,this,&eCantorixSynth::on_logDebug);
    connect(this,&eCantorixSynth::logError,this,&eCantorixSynth::on_logError);
    connect(this,&eCantorixSynth::logSuccess,this,&eCantorixSynth::on_logSuccess);

    _voices["Mikulas"]="cs";
    _voices["Lenny"]="m1";
    _voices["Lukas"]="sv";
    _voices["Mika"]="fi";


}

/// input
bool eCantorixSynth::setScore(const QJsonArray &s)
{
    if(_synthprocess)
    {
        DEVLOG_ERROR("threadRunning - cannot set score");
        return false;
    }
    _score = s;
    return true;
}

//have resampling output engine

bool eCantorixSynth::synthesize()// refactor interace
{
    if(_synthprocess)
    {
       DEVLOG_ERROR("synthprocess already running\n");
       return false;
    }
    builduScore();

    QString program = "sinsyNG";
    QStringList arguments;
    arguments << "-u" << "/tmp/uscore.sinsy";

    if(_isRealtime)
    {
        arguments << "-q" << "/tmp/sinsy.rbuf";
        _rb = new RingBuffer((char*)"/tmp/sinsy.rbuf",8*1024);
    }
    else
    {
        arguments << "-o" << "/tmp/ecantorix_sinsy.wav";
    }

    _synthprocess = new QProcess(this);
    connect(_synthprocess,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(synthprocess_finished(int,QProcess::ExitStatus)));
    _synthprocess->start(program, arguments);
    usleep(250*1000);
    return true;
}

bool eCantorixSynth::setCacheDir(QString cacheDir)
{
    (void) cacheDir;
    return true;
}


void eCantorixSynth::builduScore()
{
    int lastNoteEnd=0;

    QString uscore;

    uscore += "voice "+_internalVoice+"\n";
    uscore += "resample "+STR(_jack_samplerate)+"\n";
    DEVLOG_DEBUG("jack_samplerate "+STR(_jack_samplerate));

    for (int i = 0; i < _score.count(); ++i)
    {
        auto o = _score[i].toObject();



        if(!o.contains(NOTE_KEY_NUMBER)) {
            //tempo=o[TEMPO].toInt();
            continue;
        }

        //verfy int keyNumber=o[NOTE_KEY_NUMBER].toInt();
        int noteOffset = o[NOTE_PULSE_OFFSET].toInt();
        int noteLength = o[NOTE_PULSE_LENGTH].toInt();
        QString lyric = o[NOTE_LYRIC].toString();
        int notenum = o[NOTE_KEY_NUMBER].toInt();



        int rest = noteOffset-lastNoteEnd;

        if(rest>0)
        {
            uscore += "rest "+STR(rest)+"\n";
        }
        uscore += "note "+STR(noteLength)+" \""+lyric+"\" "+STR(notenum)+" 0 0 0 0 0\n";

        lastNoteEnd = noteOffset+noteLength;

    }
    QFile file("/tmp/uscore.sinsy");
    file.open(QFile::WriteOnly);
    file.write(uscore.toUtf8());


}

void eCantorixSynth::synthprocess_finished(int exitCode, QProcess::ExitStatus exitStatus)
{
    _synthprocess->deleteLater();
    _synthprocess=nullptr;
    DEVLOG_DEBUG("process end "+STR(exitCode)+" "+STR(int(exitStatus)));
    if(!_isRealtime)
    {
        _ctrl->startOfflinePlayback("/tmp/ecantorix_sinsy.wav");
    }
}

bool eCantorixSynth::synthIsRealtime()
{
    return _isRealtime;
}


int eCantorixSynth::readData(float *data, int size)
{
    int data_length=size*sizeof(float);
    memset(data,0,data_length);

    int count = _rb->read((char*)data,data_length);

    if(!_synthprocess && count==0) return 1;
    return 0;
}

/// phoneme transformation

QString eCantorixSynth::getTranscription(QString txt)
{
    if(txt.split(" [").length()==2)
        return txt;
    QString program = "espeak-ng";
    QStringList arguments;
    arguments << "-v" << _internalVoice;
    arguments << "-x" << txt;

    QProcess myProcess(this);
    myProcess.start(program, arguments);

    if (!myProcess.waitForStarted())
          return txt;

    if (!myProcess.waitForFinished())
            return txt;

    QByteArray result = myProcess.readAll();
    QString tmp = QString::fromUtf8(result.data());

    return txt+"["+tmp+"]";
}


bool eCantorixSynth::doPhonemeTransformation(QStringList& list)
{
    //TODO lyrizer
    DEVLOG_DEBUG(STR(list.count()));
    return false;
}

/// voice list (fix this there is only one)

bool eCantorixSynth::setVoice(QString voiceName)
{
    if(_voices.keys().contains(voiceName))
    {
        _internalVoice = _voices[voiceName];
        return true;
    }
    else
    {
        _internalVoice = "";
        return false;
    }
}


QStringList eCantorixSynth::listVoices()
{
    return _voices.keys();
}

/// logging (helper) (refactor this)
void eCantorixSynth::on_logDebug(QString debug)
{
    _ctrl->logDebug(debug);
}

void eCantorixSynth::on_logError(QString error)
{
    _ctrl->logError(error);
}

void eCantorixSynth::on_logSuccess(QString success)
{
    _ctrl->logSuccess(success);
}

//remove cacheDir support
//FIX no sound
